package cn.yy.toyflow.service.impl;

import cn.yy.toyflow.entity.*;
import cn.yy.toyflow.mapper.*;
import cn.yy.toyflow.service.DefFlowService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class DefFlowServiceImpl implements DefFlowService {
    @Autowired
    ProcessMapper processMapper;
    @Autowired
    StateMapper stateMapper;
    @Autowired
    TransitionMapper transitionMapper;
    @Autowired
    UserMapper userMapper;
    @Autowired
    GroupMapper groupMapper;
    @Autowired
    GroupMemberMapper groupMemberMapper;
    @Autowired
    ActionMapper actionMapper;

    @Override
    public List<State> defineFlowGraph(@NotNull String processID, List<State> states) {
        if (processMapper.selectById(processID) == null){
            throw new IllegalArgumentException("process not found!");
        }
        states.forEach(state -> {
            if (!processID.equals(state.getProId())){
                throw new IllegalArgumentException("found states not belonging to this process: " + processID);
            }
        });

        states.forEach(state -> stateMapper.insert(state));
        return states;
    }

    @Override
    public List<Transition> defineTrans(@NotNull String processID, List<Transition> transitions) {
        if (processMapper.selectById(processID) == null){
            throw new IllegalArgumentException("process not found!");
        }
        transitions.forEach(transition -> {
            if (!processID.equals(transition.getProcessId())){
                throw new IllegalArgumentException("found transitions not belonging to this process: " + processID);
            }
            if (transition.getCurrState() == null||stateMapper.selectById(transition.getCurrState()) == null
            ||transition.getNextState() == null || stateMapper.selectById(transition.getNextState()) == null){
                throw new IllegalArgumentException("states not found!");
            }
        });

        transitions.forEach(transition -> transitionMapper.insert(transition));
        return transitions;
    }

    @Override
    public List<Action> defineAction(String processID, List<Action> actions) {
        if (processMapper.selectById(processID) == null){
            throw new IllegalArgumentException("process not found!");
        }
        actions.forEach(action -> {
            if (!processID.equals(action.getProcessId())){
                throw new IllegalArgumentException("found actions not belonging to this process: " + processID);
            }
            if (groupMapper.selectById(action.getGroupId()) == null){
                throw new IllegalArgumentException("group "+action.getGroupId()+" not found!");
            }
            if (transitionMapper.selectById(action.getTransitionId()) == null){
                throw new IllegalArgumentException("transition "+action.getTransitionId()+" not found!");
            }
        });
        actions.forEach(action -> actionMapper.insert(action));
        return actions;
    }

    /**
     *  @param processID 对应的组
     * @param groups 组名为key， 对应一个集合， 集合元素为 user 表 external_id， 为组成员
     * @return
     */
    @Override
    public Map<String, Group> defineGroupForProcess(String processID, Map<String, List<String>> groups) {
        defineGroupArgCheck(processID, groups);
        Map<String,Group> groupList = new HashMap<>();

        for (Map.Entry<String, List<String>> entry:groups.entrySet()){
            Group group = new Group();
            group.setProcessId(processID);
            group.setName(entry.getKey());
            groupMapper.insert(group);
            for (String externalID: entry.getValue()){
                GroupMember groupMember = new GroupMember();
                groupMember.setGroupId(group.getId());
                groupMember.setUserId(userMapper.selectOne(
                        new LambdaQueryWrapper<User>().eq(User::getExternalId, externalID)
                ).getId());
                groupMemberMapper.insert(groupMember);
            }
            groupList.put(group.getName(),group);
        }
        return groupList;
    }

    private void defineGroupArgCheck(String processID, Map<String, ? extends List<String>> groups) {
        if (processMapper.selectById(processID) == null){
            throw new IllegalArgumentException("Process not found!");
        }

        for (List<String> l : groups.values()){
            for (String externalID: l){
                if(userMapper.selectOne(
                        new LambdaQueryWrapper<User>().eq(User::getExternalId, externalID)
                ) == null){
                    throw new IllegalArgumentException("User not found:"+externalID);
                }
            }
        }
    }
}
